package practise.alogrithm.hundredInterviewQuestions.convertToConcurrent;

import practise.inherit.InitFeildTest;

import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.*;

/**
 * from a ordered array, get all subset index,  whose sum equal to the input number.
 */
public class AllSubsetOfSum {
    public static Queue getAllSubsetIndexInThread(final int[] array, final int sum){
        //todo , this method has some issue, if we use a thread here, we cannot sure the return value is ready when we get the result from it, so we need join (must to be)
        Queue<List<Integer>> resutlt =  new LinkedBlockingQueue<>();
//        List<List<Integer>> resutlt =  new ArrayList<>(); todo , cannot use this class, as it will throw a modification exception
        for(int startPointer = 0;array[startPointer]<=(sum/2);startPointer++){
            final int finalStartPointer = startPointer;
            new Thread(
                   getRunner(array,sum,finalStartPointer,resutlt)
            ).start();
        }
        // todo there need a join
        return resutlt;
    }

    public static Queue getAllSubsetIndexInExecutor(final int[] array, final int sum){
        Queue<List<Integer>> resutlt =  new LinkedBlockingQueue<>();
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for(int startPointer = 0;array[startPointer]<=(sum/2);startPointer++){
            final int finalStartPointer = startPointer;
            executorService.submit(
                    getRunner(array, sum, finalStartPointer, resutlt)
            );
        }
        // todo as we use shutdown, so it will wait for the finish
        executorService.shutdown();
        while (!executorService.isTerminated());// todo in previous version, there don't have the awaitQuiescence
        return resutlt;
    }

    public static Queue getAllSubsetIndexInForkJoin(final int[] array, final int sum){
        Queue<List<Integer>> resutlt =  new LinkedBlockingQueue<>();
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        for(int startPointer = 0;array[startPointer]<=(sum/2);startPointer++){
            final int finalStartPointer = startPointer;
            forkJoinPool.execute(
                    getRunner(array, sum, finalStartPointer, resutlt)
            );
        }
        forkJoinPool.awaitQuiescence(1,TimeUnit.SECONDS);// todo , by using this line, we can avoid await the result manually
        // todo as we use shutdown, so it will wait for the finish
        forkJoinPool.shutdown();
        return resutlt;
    }

    public static Queue getAllSubsetIndexInForkJoinPool(final int[] array, final int sum){
        Queue<List<Integer>> resutlt =  new LinkedBlockingQueue<>();
        ForkJoinPool forkJoinPool = new AsyncForkJoinPool();

        for(int startPointer = 0;array[startPointer]<=(sum/2);startPointer++){
            final int finalStartPointer = startPointer;
            forkJoinPool.execute(
                    getRunner(array, sum, finalStartPointer, resutlt)
            );
        }
//        forkJoinPool.awaitQuiescence(1,TimeUnit.SECONDS);// todo , by using this line, we can avoid await the result manually
        // todo as we use shutdown, so it will wait for the finish
        forkJoinPool.shutdown();
        return resutlt;
    }

    static class AsyncForkJoinPool extends ForkJoinPool{
        public AsyncForkJoinPool(){
            super(Runtime.getRuntime().availableProcessors(),
                    defaultForkJoinWorkerThreadFactory, null, true);
        }
    }


    private static Runnable getRunner(final int[] array, final int sum,final int finalStartPointer, Queue<List<Integer>> resutlt){
       return  new Runnable(){

            @Override
            public void run() {
//                System.out.println("StartPointer:" + finalStartPointer);
                int innerSum = 0;
                for(int endPointer= finalStartPointer; endPointer<array.length;endPointer++){
                    innerSum +=array[endPointer];
                    if(innerSum>sum){
                        break;
                    }
                    if(innerSum==sum){
//                        System.out.println("innerSum:" + innerSum);
                        List<Integer> innerResult = new ArrayList<>();
                        innerResult.add(finalStartPointer);
                        innerResult.add(endPointer);
                        resutlt.add(innerResult);
                    }

                }
            }
        };
    }

    public static void main(String[] args){
        Queue result = getAllSubsetIndexInThread(new int[]{1, 2, 3, 4, 5, 6, 7, 8}, 15);
        try {// todo must wait for the thread to be finished
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("result:" + result);

        System.out.println("result:" + getAllSubsetIndexInExecutor(new int[]{1, 2, 3, 4, 5, 6, 7, 8}, 15));

        result = getAllSubsetIndexInForkJoin(new int[]{1, 2, 3, 4, 5, 6, 7, 8}, 15);
//        try {// todo must wait for the thread to be finished if we do not wait on the pool
//            Thread.sleep(1000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        System.out.println("result:" + result);

        result = getAllSubsetIndexInForkJoinPool(new int[]{1, 2, 3, 4, 5, 6, 7, 8}, 15);
        System.out.println("result:" + result);
    }
}
